package com.eerl.pdev.werl.impl;

import com.eerl.pdev.exception.ErlTypeCastRuntimeException;
import com.ericsson.otp.erlang.*;

import static com.eerl.pdev.werl.impl.ErlangType.*;

public class ErlTypeCast {

    public static OtpErlangAtom castToAtom(String str) {
        return new OtpErlangAtom(str);
    }

    public static OtpErlangString castToString(String str) {
        return new OtpErlangString(str);
    }

    public static OtpErlangInt castToInt(String str) {
        return new OtpErlangInt(Integer.parseInt(str));
    }

    public static OtpErlangObject castToObj(ErlangType erlangType) {
        return switch (erlangType.getType()) {
            case ERLANG_TYPE_ATOM, ErlangType.ERLANG_TYPE_BOOL -> castToAtom(String.valueOf(erlangType.getValue()));
            case ErlangType.ERLANG_TYPE_NUMBER -> castToInt((String) erlangType.getValue());
            case ErlangType.ERLANG_TYPE_STRING -> castToString((String) erlangType.getValue());
            case ErlangType.ERLANG_TYPE_LIST-> castToList((ErlangType[]) erlangType.getValue());
            case ErlangType.ERLANG_TYPE_TUPLE-> castToTuple((ErlangType[]) erlangType.getValue());
            default -> throw new ErlTypeCastRuntimeException();
        };
    }

    public static OtpErlangTuple castToTuple(ErlangType[] strs) {
        OtpErlangObject[] list = new OtpErlangObject[strs.length];
        for (int i = 0; i < strs.length; i++) {
            ErlangType erlangType = strs[i];
            list[i] = castToObj(erlangType);
        }
        return new OtpErlangTuple(list);
    }

    public static OtpErlangList castToList(ErlangType[] strs) {
        OtpErlangObject[] list = new OtpErlangObject[strs.length];
        for (int i = 0; i < strs.length; i++) {
            ErlangType erlangType = strs[i];
            list[i] = castToObj(erlangType);
        }
        return new OtpErlangList(list);
    }

    public static ErlangType toErlangType(OtpErlangObject erlObj, ErlangType[] constructParseEnumList) {
        if (erlObj instanceof OtpErlangTuple) {
            return toErlangType((OtpErlangTuple) erlObj, constructParseEnumList);
        } else if (erlObj instanceof OtpErlangList) {
            return toErlangType((OtpErlangList) erlObj, constructParseEnumList);
        } else {
            throw new ErlTypeCastRuntimeException();
        }
    }

    public static ErlangType toErlangType(OtpErlangTuple erlTuple, ErlangType[] constructParseEnumList) {
        ErlangType[] values = new ErlangType[erlTuple.arity()];
        for (int i = 0; i < constructParseEnumList.length; i++) {
            ErlangType constructParseEnum = constructParseEnumList[i];
            ErlangType parsedResult = switch (constructParseEnum.getType()) {
                case ERLANG_TYPE_ATOM, ERLANG_TYPE_NUMBER, ERLANG_TYPE_BOOL, ERLANG_TYPE_STRING ->
                    new ErlangType(erlTuple.elementAt(i).toString(), constructParseEnum.getType());
                case ERLANG_TYPE_LIST -> {
                    OtpErlangList subList = ((OtpErlangList) erlTuple.elementAt(i));
                    yield toErlangType(subList, (ErlangType[]) constructParseEnum.getValue());
                }
                case ERLANG_TYPE_TUPLE -> {
                    OtpErlangTuple subList = ((OtpErlangTuple) erlTuple.elementAt(i));
                    yield toErlangType(subList, (ErlangType[]) constructParseEnum.getValue());
                }
                default -> throw new ErlTypeCastRuntimeException();
            };
            values[i] = parsedResult;
        }
        return new ErlangType(values, ERLANG_TYPE_LIST);
    }

    public static ErlangType toErlangType(OtpErlangList erlTuple, ErlangType[] constructParseEnumList) {
        ErlangType[] values = new ErlangType[erlTuple.arity()];
        for (int i = 0; i < constructParseEnumList.length; i++) {
            ErlangType constructParseEnum = constructParseEnumList[i];
            ErlangType parsedResult = switch (constructParseEnum.getType()) {
                case ERLANG_TYPE_ATOM, ERLANG_TYPE_NUMBER, ERLANG_TYPE_BOOL, ERLANG_TYPE_STRING ->
                    new ErlangType(erlTuple.elementAt(i).toString(), constructParseEnum.getType());
                case ERLANG_TYPE_LIST-> {
                    OtpErlangList subList = ((OtpErlangList) erlTuple.elementAt(i));
                    yield toErlangType(subList, (ErlangType[]) constructParseEnum.getValue());
                }
                case ERLANG_TYPE_TUPLE -> {
                    OtpErlangTuple subList = ((OtpErlangTuple) erlTuple.elementAt(i));
                    yield toErlangType(subList, (ErlangType[]) constructParseEnum.getValue());
                }
                default -> throw new ErlTypeCastRuntimeException();
            };
            values[i] = parsedResult;
        }
        return new ErlangType(values, ERLANG_TYPE_LIST);
    }
}
